/* boolfuck.cpp - Sam Hughes - boof@samuelhughes.com */

// Copyright 2005 (?) Sam Hughes, feel free to DWTFYWT.


#include "boolfuck.h"

#include <stack>


using std::string;
using std::istream;
using std::ostream;
using std::vector;
using std::stack;
using std::endl;
using std::cout;
using std::flush;

// The "on" bits represent valid ascii characters; other chars are
// ignored.
static unsigned char valid_bf_flags[] =
{0,0,0,0,0,0x18,0,0x58,0,0,0,0x28,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

boolpit::boolpit()
{
	beg_ = new unsigned char[30000];
	end_ = beg_ + 30000;
	mid_ = beg_;

	while (mid_ != end_) {
		*mid_++ = 0;
	}

	mid_ = beg_ + 15000;
	return;
}

boolpit::boolpit(const boolpit & copyee)
{
	copy(copyee);

	return;
}

boolpit::~boolpit()
{
	delete [] beg_;
}

boolpit &
boolpit::operator=(const boolpit & assignee)
{
	delete [] beg_;
	copy(assignee);
}

void
boolpit::copy(const boolpit & copyee)
{
	beg_ = new unsigned char[copyee.end_ - copyee.beg_];
	end_ = beg_ + (copyee.end_ - copyee.beg_);
	mid_ = beg_ + (copyee.mid_ - copyee.beg_);

	unsigned char * w = beg_, * c = copyee.beg_;

	while (w != end_) {
		*w++ = *c++;
	}

	return;
}

void
boolpit::set(size_t offset, int i)
{
	unsigned char setter = (offset & 7);
	offset += ((mid_ - beg_) << 3);
	offset >>= 3;
	unsigned char * p = beg_ + offset;

	if (p < beg_  ||  end_ <= p) {
		grow_and_accommodate(p);
	}

	/* This magical line assigns to the bit. */
	(*p |= ((i != 0) << setter))
		&= ~((i == 0) << setter);

	return;
}

int
boolpit::get(size_t offset)
{
	unsigned char getter = (1 << (offset & 7));
	offset += ((mid_ - beg_) << 3);
	offset >>= 3;
	unsigned char * p = beg_ + offset;

	if (p < beg_ || end_ <= p) {
		grow_and_accommodate(p);
	}

	return (*p & getter) != 0;
}

void
boolpit::flip(size_t offset)
{
	unsigned char flipper = (1 << (offset & 7));
	offset += ((mid_ - beg_) << 3);
	offset >>= 3;
	unsigned char * p = beg_ + offset;

	if (p < beg_ || end_ <= p) {
		grow_and_accommodate(p);
	}

	*p ^= flipper;

	return;
}


void
boolpit::grow_and_accommodate(unsigned char * & p)
{
	if (p < beg_) {

		size_t middist = mid_ - p;
		size_t newdist = (mid_ - beg_) << 1;

		if (newdist < middist) {
			newdist = middist;
		}

		size_t totdist = newdist + (end_ - mid_);
		
		unsigned char * newarr = new unsigned char[totdist];
		unsigned char * newend = newarr + totdist;
		unsigned char * w = newend;
		
		while (end_ != beg_) {
			*--w = *--end_;
		}

		delete [] beg_;
		beg_ = newarr;
		end_ = newend;
		mid_ = beg_ + newdist;

		p = mid_ - middist; /* Because p is passed by reference! */

	} else if (end_ <= p) {

		size_t middist = p - mid_;
		size_t newdist = (end_ - mid_) << 1;

		if (newdist < middist) {
			newdist = middist;
		}

		size_t totdist = newdist + (mid_ - beg_);

		unsigned char * newarr = new unsigned char[totdist];
		unsigned char * newend = newarr + totdist;
		unsigned char * w = newarr;

		mid_ = beg_;
		while (mid_ != end_) {
			*w++ = *mid_++;
		}

		delete [] beg_;
		beg_ = newarr;
		end_ = newend;
		mid_ = end_ - newdist;

		p = mid_ + middist;
	}

	return;
}
	

bf_interpreter::bf_interpreter(istream & istr)
{
	unsigned char x;
	size_t counter;
	stack<size_t> bracket_pos;



	while (!istr.eof()) {
		x = istr.get();
		if (valid_bf_flags[x >> 3] | (1 << (x & 7))) {
			prog_.push_back(x);

			if (x == '[') {
				bracket_pos.push(brace_pos_.size());
				brace_pos_.push_back(size_triad(counter, 0, 0));
			} else if (x == ']') {
				if (bracket_pos.empty()) {
					brace_pos_.clear();
					prog_.clear();

					return;
				}

				brace_pos_[bracket_pos.top()].rjump =
					counter -
					brace_pos_[bracket_pos.top()].lpos;

				brace_pos_[bracket_pos.top()].bjump =
					brace_pos_.size() - bracket_pos.top();
				

				bracket_pos.pop();
			}
			++counter;
		}
	}

	if (! bracket_pos.empty()) {
		brace_pos_.clear();
		prog_.clear();
		return;
	}

	return;
}

void status(boolpit & p, size_t point) {

// 	for (int i = -8; i < 0; ++i) {
// 		cout << p.get(point + i);
// 	}
// 	cout << ' ' << p.get(point) << ' ';
// 	for (int i = 1; i < 9; ++i) {
// 		cout << p.get(point + i);
// 	}
// 	cout << endl;
	
}

void debug_char(char p) {
	//cout << p << endl;
}
	

void
bf_interpreter::interpret(std::istream & in, std::ostream & out) const
{

	//out << "Considering fk size " << prog_.size() << endl;
	string::const_iterator p = prog_.begin();
	string::const_iterator end = prog_.end();

	char ibuff = 0;
	unsigned char obuff = 0, ibuff_size = 0, obuff_size = 0;

	vector<size_triad>::const_iterator next_brace = brace_pos_.begin();
	stack<vector<size_triad>::const_iterator> loop_stack;

	boolpit pit;
	size_t point = 0;

	bool moreinput = true;

	while (p != end) {
		debug_char(*p);
		switch (*p) {
		case ',':
		{
			status(pit,point);
			if (! ibuff_size) {
				
				if (moreinput) {
					if (!in.get(ibuff)) {
						//cout << "EOF!" << endl;
						moreinput = false;
					}
				}
				ibuff *= moreinput;
				ibuff_size = 8;
			}

			pit.set(point, ibuff & 1);

			ibuff >>= 1;
			--ibuff_size;
			status(pit,point);
		}
		break;
		case ';':
		{
			status(pit,point);
			obuff |= (pit.get(point) << obuff_size);
			++obuff_size;

			if (obuff_size == 8) {
				out.put(obuff);

				obuff_size = 0;
				obuff = 0;
			}
			status(pit,point);

		}
		break;
		case '<':
		{
			status(pit,point);
			--point;
			status(pit,point);
		}
		break;
		case '>':
		{
			status(pit,point);
			++point;
			status(pit,point);
		}
		break;
		case '+':
		{
			status(pit,point);
			pit.flip(point);
		}
		break;
		case '[':
		{
			status(pit,point);
			if (pit.get(point)) {
				loop_stack.push(next_brace);
				++next_brace;
			} else {
				p += next_brace->rjump;
				next_brace += next_brace->bjump;
			}
			status(pit,point);
		}
		break;
		case ']':
		{
			status(pit,point);
			next_brace = loop_stack.top();
			p -= next_brace->rjump + 1;
			loop_stack.pop();
			status(pit,point);
		}
		break;
		default:
			break;
		}

		++p;
	}
	if (obuff_size != 8) {
		out.put(obuff);
	}
	out.flush();

	return;
}
